package com.aghcampusnavigation.navi.logic;

import android.util.Log;

import com.aghcampusnavigation.navi.model.MapRouteStep;
import com.google.android.gms.maps.model.LatLng;

import org.json.*;

import java.util.ArrayList;

/**
 * Created by Filip on 27.04.14.
 */
public class MapRoutingHandler {

    private String parsingStatus;
    private String downloadedJSONString;
    private ArrayList<LatLng> polylines;
    private ArrayList<LatLng> overviewPolylinePoints;

    private ArrayList<MapRouteStep> mapRouteSteps;

    private String encodedOverviewPolyline;

    private ServiceHandler serviceHandler;

    public void downloadAndParse(String origin, String destination) {
        Log.d(getClass().getSimpleName(), "downloading...");
        downloadedJSONString = download(origin, destination);
        Log.d(getClass().getSimpleName(), "downloadedJSONString is: " + downloadedJSONString);
        Log.d(getClass().getSimpleName(), "parsing...");
        parse(downloadedJSONString);
    }

    private void parse(String jsonStr) {
        // file exists, it is the first boot
        if (jsonStr != null) {

            try {
                JSONObject jsonObj = new JSONObject(jsonStr);
                JSONArray jsonRoutes = jsonObj.getJSONArray("routes");
                if(jsonObj.getString("status").equals("OK")) {
                    Log.d(getClass().getSimpleName(), "status: " + jsonObj.getString("status"));
                } else {
                    Log.e(getClass().getSimpleName(), "status: " + jsonObj.getString("status"));
                }
                parsingStatus = jsonObj.getString("status");

                encodedOverviewPolyline = jsonRoutes.getJSONObject(0).getJSONObject("overview_polyline").getString("points");
                Log.d(getClass().getSimpleName(), "encodedOverviewPolyline is: " + encodedOverviewPolyline);
                overviewPolylinePoints = parseEncodedPolylines(encodedOverviewPolyline);
                Log.d(getClass().getSimpleName(), "overviewPolylinePoints is: " + overviewPolylinePoints.toString());

                JSONArray jsonSteps = jsonRoutes.getJSONObject(0).getJSONArray("legs").getJSONObject(0).getJSONArray("steps");
                JSONObject jsonStep;

                mapRouteSteps = new ArrayList<MapRouteStep>();

                for(int i = 0; i < jsonSteps.length(); i++) {
                    MapRouteStep mapRouteStep = new MapRouteStep();
                    jsonStep = jsonSteps.getJSONObject(i);

                    String endodedPolyline = jsonStep.getJSONObject("polyline").getString("points");
                    mapRouteStep.setRoutePoints(parseEncodedPolylines(endodedPolyline));
                    mapRouteStep.setDuration(jsonStep.getJSONObject("duration").getInt("value"));
                    mapRouteStep.setDistance(jsonStep.getJSONObject("distance").getInt("value"));

                    mapRouteSteps.add(mapRouteStep);
                }

            } catch (JSONException e) {
                e.printStackTrace();
            }

/*
            // linea init
            LatLng polyline;

            // array list of lines init
            polylines = new ArrayList<LatLng>();

            // get json array
            JSONArray jsonArray = new JSONArray();
            jsonArray = jsonArray.getJSONArray(jsonStr, "polytag");

            for (int i = 0; i < jsonArray.length(); i++) {

                JSONObject jsonPolyline;

                try {
                    jsonPolyline = jsonArray.getJSONObject(i);

                    polyline = new LatLng(Double.valueOf(jsonPolyline.getString("lat")),
                            Double.valueOf(jsonPolyline.getString("lon")));

                    polylines.add(polyline);

                } catch (JSONException e) {
                    Log.d(getClass().getSimpleName(), "JSONException reading polylines: " + e.getMessage());
                } catch (Exception e) {
                    Log.d(getClass().getSimpleName(), "Exception reading polylines: " + e.getMessage());
                }
            }
            */
        }
    }

    // decoding algorithm from http://mobile.synyx.de/2010/06/routing-driving-directions-on-android-part-1-get-the-route/
    private ArrayList<LatLng> parseEncodedPolylines(String encoded) {
        ArrayList<LatLng> poly = new ArrayList<LatLng>();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;

        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {

                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng p = new LatLng(((double) lat / 1E5), ((double) lng / 1E5) );
            poly.add(p);
        }
        return poly;
    }

    private String download(String origin, String destination) {
        String url = "http://maps.googleapis.com/maps/api/directions/json?origin="+origin+"&destination="+destination+"&sensor=false&mode=walking";
        serviceHandler = new ServiceHandler();
        return serviceHandler.makeServiceCall(url, ServiceHandler.GET);
    }

    public ArrayList<LatLng> getOverviewPoints() {
        return overviewPolylinePoints;
    }

    public ArrayList<LatLng> getDetailedPoints() {
        ArrayList<LatLng> points = new ArrayList<LatLng>();
        for (int i = 0; i < mapRouteSteps.size(); i++) {
            points.addAll(mapRouteSteps.get(i).getRoutePoints());
        }
        return points;
    }

    public int getDuration() {
        int value = 0;
        for (MapRouteStep mrs : mapRouteSteps ) {
            value += mrs.getDuration();
        }
        return value;
    }

    public int getDistance() {
        int value = 0;
        for (MapRouteStep mrs : mapRouteSteps ) {
            value += mrs.getDistance();
        }
        return value;
    }

    public boolean isParsingStatusOK() {
        return parsingStatus.equals("OK");
    }
}
